/**
 * @file logger.c
 * @brief Logger implementation for H743-V2
 * @author Flight Control Team
 * @date 2025-10-31
 */

#include "logger.h"
#include "bsp_usb.h"
#include "bsp_sd_diskio.h"
#include "FreeRTOS.h"
#include "semphr.h"
#include <stdio.h>
#include <string.h>

/* Logger buffer size */
#define LOGGER_BUFFER_SIZE      4096
#define LOGGER_BINARY_BUFFER    2048

/* ANSI color codes */
#define ANSI_COLOR_RED     "\x1b[31m"
#define ANSI_COLOR_YELLOW  "\x1b[33m"
#define ANSI_COLOR_GREEN   "\x1b[32m"
#define ANSI_COLOR_CYAN    "\x1b[36m"
#define ANSI_COLOR_RESET   "\x1b[0m"

/* Logger context */
static struct {
    LoggerConfig_t config;
    SemaphoreHandle_t mutex;
    uint8_t text_buffer[LOGGER_BUFFER_SIZE];
    uint8_t binary_buffer[LOGGER_BINARY_BUFFER];
    uint32_t text_head;
    uint32_t text_tail;
    uint32_t binary_head;
    uint32_t binary_tail;
    uint32_t total_logs;
    uint32_t dropped_logs;
    bool initialized;
    bool file_open;
    char current_filename[64];
} logger_ctx = {0};

/* Level strings */
static const char *level_strings[] = {
    "NONE",
    "ERROR",
    "WARN",
    "INFO",
    "DEBUG",
    "TRACE"
};

/* Level colors */
static const char *level_colors[] = {
    ANSI_COLOR_RESET,
    ANSI_COLOR_RED,
    ANSI_COLOR_YELLOW,
    ANSI_COLOR_GREEN,
    ANSI_COLOR_CYAN,
    ANSI_COLOR_RESET
};

/**
 * @brief Initialize logger
 */
int Logger_Init(void)
{
    if (logger_ctx.initialized) {
        return 0;
    }
    
    /* Create mutex */
    logger_ctx.mutex = xSemaphoreCreateMutex();
    if (logger_ctx.mutex == NULL) {
        return -1;
    }
    
    /* Initialize default configuration */
    logger_ctx.config.level = LOG_LEVEL_INFO;
    logger_ctx.config.output = LOG_OUTPUT_USB | LOG_OUTPUT_SDCARD;
    logger_ctx.config.enable_colors = true;
    logger_ctx.config.enable_timestamp = true;
    logger_ctx.config.enable_binary = true;
    logger_ctx.config.buffer_size = LOGGER_BUFFER_SIZE;
    
    /* Initialize buffers */
    memset(logger_ctx.text_buffer, 0, LOGGER_BUFFER_SIZE);
    memset(logger_ctx.binary_buffer, 0, LOGGER_BINARY_BUFFER);
    logger_ctx.text_head = 0;
    logger_ctx.text_tail = 0;
    logger_ctx.binary_head = 0;
    logger_ctx.binary_tail = 0;
    logger_ctx.total_logs = 0;
    logger_ctx.dropped_logs = 0;
    logger_ctx.file_open = false;
    
    logger_ctx.initialized = true;
    
    return 0;
}

/**
 * @brief Deinitialize logger
 */
void Logger_DeInit(void)
{
    if (!logger_ctx.initialized) {
        return;
    }
    
    /* Flush remaining logs */
    Logger_Flush();
    
    /* Close file if open */
    if (logger_ctx.file_open) {
        Logger_CloseFile();
    }
    
    /* Delete mutex */
    if (logger_ctx.mutex != NULL) {
        vSemaphoreDelete(logger_ctx.mutex);
        logger_ctx.mutex = NULL;
    }
    
    logger_ctx.initialized = false;
}

/**
 * @brief Configure logger
 */
int Logger_Configure(const LoggerConfig_t *config)
{
    if (config == NULL) {
        return -1;
    }
    
    if (xSemaphoreTake(logger_ctx.mutex, pdMS_TO_TICKS(100)) == pdTRUE) {
        memcpy(&logger_ctx.config, config, sizeof(LoggerConfig_t));
        xSemaphoreGive(logger_ctx.mutex);
        return 0;
    }
    
    return -1;
}

/**
 * @brief Set log level
 */
void Logger_SetLevel(LogLevel_t level)
{
    if (xSemaphoreTake(logger_ctx.mutex, pdMS_TO_TICKS(100)) == pdTRUE) {
        logger_ctx.config.level = level;
        xSemaphoreGive(logger_ctx.mutex);
    }
}

/**
 * @brief Set log output destinations
 */
void Logger_SetOutput(uint8_t output)
{
    if (xSemaphoreTake(logger_ctx.mutex, pdMS_TO_TICKS(100)) == pdTRUE) {
        logger_ctx.config.output = output;
        xSemaphoreGive(logger_ctx.mutex);
    }
}

/**
 * @brief Log message with level
 */
void Logger_Log(LogLevel_t level, const char *tag, const char *format, ...)
{
    if (!logger_ctx.initialized || level > logger_ctx.config.level) {
        return;
    }
    
    char buffer[256];
    int len = 0;
    
    /* Format timestamp */
    if (logger_ctx.config.enable_timestamp) {
        uint32_t timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS;
        len += snprintf(buffer + len, sizeof(buffer) - len, "[%6lu.%03lu] ", 
                       timestamp / 1000, timestamp % 1000);
    }
    
    /* Format level with color */
    if (logger_ctx.config.enable_colors && 
        (logger_ctx.config.output & (LOG_OUTPUT_USB | LOG_OUTPUT_UART))) {
        len += snprintf(buffer + len, sizeof(buffer) - len, "%s%-5s%s ", 
                       level_colors[level], level_strings[level], ANSI_COLOR_RESET);
    } else {
        len += snprintf(buffer + len, sizeof(buffer) - len, "%-5s ", 
                       level_strings[level]);
    }
    
    /* Format tag */
    if (tag != NULL) {
        len += snprintf(buffer + len, sizeof(buffer) - len, "[%s] ", tag);
    }
    
    /* Format message */
    va_list args;
    va_start(args, format);
    len += vsnprintf(buffer + len, sizeof(buffer) - len, format, args);
    va_end(args);
    
    /* Add newline */
    if (len < (int)sizeof(buffer) - 2) {
        buffer[len++] = '\r';
        buffer[len++] = '\n';
        buffer[len] = '\0';
    }
    
    /* Output to USB */
    if (logger_ctx.config.output & LOG_OUTPUT_USB) {
        BSP_USB_Send((uint8_t *)buffer, len);
    }
    
    /* Output to UART */
    if (logger_ctx.config.output & LOG_OUTPUT_UART) {
        // TODO: Send to UART
    }
    
    /* Buffer for SD card */
    if (logger_ctx.config.output & LOG_OUTPUT_SDCARD) {
        if (xSemaphoreTake(logger_ctx.mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
            // TODO: Add to buffer for SD card writing
            xSemaphoreGive(logger_ctx.mutex);
        }
    }
    
    logger_ctx.total_logs++;
}

/**
 * @brief Log binary data
 */
int Logger_LogBinary(uint16_t msg_id, const void *data, uint16_t length)
{
    if (!logger_ctx.initialized || !logger_ctx.config.enable_binary) {
        return -1;
    }
    
    if (data == NULL || length == 0 || length > 256) {
        return -1;
    }
    
    BinaryLogEntry_t entry;
    entry.timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS * 1000;  // Convert to microseconds
    entry.msg_id = msg_id;
    entry.length = length;
    memcpy(entry.data, data, length);
    
    if (xSemaphoreTake(logger_ctx.mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
        // TODO: Add to binary buffer for SD card writing
        xSemaphoreGive(logger_ctx.mutex);
        return 0;
    }
    
    return -1;
}

/**
 * @brief Flush log buffer to SD card
 */
int Logger_Flush(void)
{
    if (!logger_ctx.initialized) {
        return -1;
    }
    
    if (xSemaphoreTake(logger_ctx.mutex, pdMS_TO_TICKS(100)) == pdTRUE) {
        // TODO: Write buffered data to SD card
        xSemaphoreGive(logger_ctx.mutex);
        return 0;
    }
    
    return -1;
}

/**
 * @brief Get number of log entries in buffer
 */
uint32_t Logger_GetBufferCount(void)
{
    uint32_t count = 0;
    
    if (xSemaphoreTake(logger_ctx.mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
        if (logger_ctx.text_head >= logger_ctx.text_tail) {
            count = logger_ctx.text_head - logger_ctx.text_tail;
        } else {
            count = LOGGER_BUFFER_SIZE - logger_ctx.text_tail + logger_ctx.text_head;
        }
        xSemaphoreGive(logger_ctx.mutex);
    }
    
    return count;
}

/**
 * @brief Get logger statistics
 */
void Logger_GetStats(uint32_t *total_logs, uint32_t *dropped_logs, uint8_t *buffer_usage)
{
    if (xSemaphoreTake(logger_ctx.mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
        if (total_logs != NULL) {
            *total_logs = logger_ctx.total_logs;
        }
        if (dropped_logs != NULL) {
            *dropped_logs = logger_ctx.dropped_logs;
        }
        if (buffer_usage != NULL) {
            uint32_t used = Logger_GetBufferCount();
            *buffer_usage = (uint8_t)((used * 100) / LOGGER_BUFFER_SIZE);
        }
        xSemaphoreGive(logger_ctx.mutex);
    }
}

/**
 * @brief Create new log file on SD card
 */
int Logger_CreateFile(const char *filename)
{
    if (!logger_ctx.initialized) {
        return -1;
    }
    
    if (!BSP_SDCard_IsPresent()) {
        return -1;
    }
    
    if (xSemaphoreTake(logger_ctx.mutex, pdMS_TO_TICKS(100)) == pdTRUE) {
        /* Generate filename if not provided */
        if (filename == NULL) {
            uint32_t timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS;
            snprintf(logger_ctx.current_filename, sizeof(logger_ctx.current_filename),
                    "log_%lu.bin", timestamp);
        } else {
            strncpy(logger_ctx.current_filename, filename, 
                   sizeof(logger_ctx.current_filename) - 1);
        }
        
        // TODO: Create file on SD card
        logger_ctx.file_open = true;
        
        xSemaphoreGive(logger_ctx.mutex);
        return 0;
    }
    
    return -1;
}

/**
 * @brief Close current log file
 */
void Logger_CloseFile(void)
{
    if (!logger_ctx.initialized || !logger_ctx.file_open) {
        return;
    }
    
    /* Flush remaining data */
    Logger_Flush();
    
    if (xSemaphoreTake(logger_ctx.mutex, pdMS_TO_TICKS(100)) == pdTRUE) {
        // TODO: Close file on SD card
        logger_ctx.file_open = false;
        xSemaphoreGive(logger_ctx.mutex);
    }
}

/**
 * @brief Download log file via USB
 */
int Logger_DownloadFile(const char *filename)
{
    if (!logger_ctx.initialized || filename == NULL) {
        return -1;
    }
    
    if (!BSP_SDCard_IsPresent()) {
        return -1;
    }
    
    // TODO: Implement file download via USB
    // 1. Open file on SD card
    // 2. Read file in chunks
    // 3. Send chunks via USB
    // 4. Close file
    
    return 0;
}

